package com.beiding.fastmock.fram.common;

import com.beiding.fastmock.fram.HttpBase;
import com.beiding.fastmock.fram.RequestHolder;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletResponse;
import java.net.URI;
import java.security.cert.X509Certificate;
import java.util.*;


/**
 * @author 丁常磊
 * @date 2021/7/14 17:28
 */

public class Requester {

    private RestTemplate restTemplate = getRestTemplate();//httpsRestTemplate();

    public ResponseWrapper exchange(Map<String, Object> request) {


        String basePath = (String) request.get("basePath");

        String url = "";

        if (basePath != null) {
            url += basePath;
        }

        String path = (String) request.get("path");
        if (path != null) {

            Map<String, String> rewritePath = (Map<String, String>) request.get("rewritePath");
            if (rewritePath != null) {
                String from = rewritePath.get("0");
                String to = rewritePath.get("1");
                path = path.replaceAll(from, to);
            }

            url += path;
        }

        //判断url是否有请求参数

        Map<String, Object> param = (Map<String, Object>) request.get("param");
        if (param != null && param.size() > 0) {
            StringBuilder builder = new StringBuilder();
            Iterator<Map.Entry<String, Object>> iterator = param.entrySet().iterator();
            if (iterator.hasNext()) {
                Map.Entry<String, Object> next = iterator.next();
                builder.append(next.getKey()).append("=").append(next.getValue());
                while (iterator.hasNext()) {
                    next = iterator.next();
                    builder.append("&").append(next.getKey()).append("=").append(next.getValue());
                }
            }
            url += "?" + builder.toString();
        }

        HttpHeaders httpHeaders = new HttpHeaders();

        MultiValueMap<String, String> header = (MultiValueMap<String, String>) request.get("header");

        if (header != null) {
            header.forEach(httpHeaders::put);
        }

        String method = (String) request.get("method");

        ResponseEntity<byte[]> body = restTemplate.exchange(URI.create(url), HttpMethod.valueOf(method), new HttpEntity<>(request.get("body"), httpHeaders), byte[].class);
        return new ResponseWrapper(body);
    }

    public ResponseWrapper exchange(Map<String, Object> request, RequestHolder requestHolder) {

        if (!request.containsKey("header")) {
            request.put("header", requestHolder.getHeader());
        } else {
            Map<String, Object> lv1 = (Map<String, Object>) request.get("header");
            MultiValueMap<String, String> lv2 = new LinkedMultiValueMap<>(requestHolder.getHeader());

            lv1.forEach((k, v) -> {

                if (v instanceof String) {
                    lv2.set(k, (String) v);
                } else if (v instanceof ScriptObjectMirror) {
                    if (((ScriptObjectMirror) v).isArray()) {
                        lv2.remove(k);
                        for (Object value : ((ScriptObjectMirror) v).values()) {
                            lv2.add(k, (String) value);
                        }
                    }
                }

            });

            request.put("header", lv2);
        }

        if (!request.containsKey("param")) {
            request.put("param", requestHolder.getRawParam());
        } else {

            Map<String, Object> lv1 = (Map<String, Object>) request.get("param");
            Map<String, Object> lv2 = new HashMap<>(requestHolder.getRawParam());
            lv2.putAll(lv1);
            request.put("param", lv2);

        }

        if (!request.containsKey("body")) {
            request.put("body", requestHolder.getBody());
        }

        if (!request.containsKey("path")) {
            request.put("path", requestHolder.getPath());
        }

        if (!request.containsKey("basePath")) {
            request.put("basePath", requestHolder.getBasePath());
        }

        if (!request.containsKey("method")) {
            request.put("method", requestHolder.getMethod());
        }

        return exchange(request);
    }

    /**
     * 响应结果封装
     */
    public static class ResponseWrapper extends HttpBase {

        private Integer status;

        public ResponseWrapper(ResponseEntity<byte[]> body) {
            this.status = body.getStatusCodeValue();
            setHeader(new LinkedMultiValueMap<>(body.getHeaders()));
            setBody(body.getBody());
        }

        public int getStatus() {
            return status;
        }

        /**
         * 默认全部写入
         *
         * @param requestHolder
         */
        public void write(RequestHolder requestHolder) {
            write(requestHolder, "status header body");
        }

        /**
         * 可指定写入哪些内容
         *
         * @param requestHolder
         * @param content
         */
        public void write(RequestHolder requestHolder, String content) {
            HttpServletResponse response = requestHolder.getResponse();
            List<String> cts = Arrays.asList(content.split(" "));
            if (cts.contains("status")) {
                response.setStatus(getStatus());
            }

            if (cts.contains("header")) {
                MultiValueMap<String, String> headers = getHeader();

                //移除掉与编码及内容相关的响应头
                headers.remove("Transfer-Encoding");
                headers.remove("Content-Length");
                headers.forEach((k, v) -> {
                    for (String s : v) {
                        response.addHeader(k, s);
                    }
                });
            }
            if (cts.contains("body")) {
                try {
                    byte[] body = getBody();
                    if (body != null) {
                        response.getOutputStream().write(getBody());
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }


        public void setStatus(Integer status) {
            this.status = status;
        }
    }


    //*****************支持https协议**********************

    private CloseableHttpClient acceptsUntrustedCertsHttpClient() {
        try {
            HttpClientBuilder b = HttpClientBuilder.create();

            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (TrustStrategy) (arg0, arg1) -> true).build();
            b.setSSLContext(sslContext);
            HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
            SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.getSocketFactory())
                    .register("https", sslSocketFactory)
                    .build();
            PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            connMgr.setMaxTotal(200);
            connMgr.setDefaultMaxPerRoute(100);
            b.setConnectionManager(connMgr);
            CloseableHttpClient client = b.build();
            return client;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }


    /**
     * 支持https协议
     *
     * @return
     */
    private RestTemplate httpsRestTemplate() {

        RestTemplate restTemplate = new RestTemplate(httpComponentsClientHttpRequestFactory());
        restTemplate.setErrorHandler(
                new ResponseErrorHandler() {
                    @Override
                    public boolean hasError(ClientHttpResponse clientHttpResponse) {
                        return false;
                    }

                    @Override
                    public void handleError(ClientHttpResponse clientHttpResponse) {
                        // 默认处理非200的返回，会抛异常
                    }
                });
        return restTemplate;

    }

    private HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory() {
        CloseableHttpClient httpClient = acceptsUntrustedCertsHttpClient();
        HttpComponentsClientHttpRequestFactory httpsFactory =
                new HttpComponentsClientHttpRequestFactory(httpClient);
        httpsFactory.setReadTimeout(40000);
        httpsFactory.setConnectTimeout(40000);
        return httpsFactory;
    }

    public static RestTemplate getRestTemplate() {
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] arg0, String arg1) {
                    return true;
                }
            }).build();

            SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext,
                    new String[]{"TLSv1"},
                    null,
                    NoopHostnameVerifier.INSTANCE);

            CloseableHttpClient httpClient = HttpClients.custom()
                    .setSSLSocketFactory(csf)
                    .build();

            HttpComponentsClientHttpRequestFactory requestFactory =
                    new HttpComponentsClientHttpRequestFactory();

            requestFactory.setHttpClient(httpClient);
            RestTemplate restTemplate = new RestTemplate(requestFactory);
            restTemplate.setErrorHandler(
                    new ResponseErrorHandler() {
                        @Override
                        public boolean hasError(ClientHttpResponse clientHttpResponse) {
                            return false;
                        }

                        @Override
                        public void handleError(ClientHttpResponse clientHttpResponse) {
                            // 默认处理非200的返回，会抛异常
                        }
                    });
            return restTemplate;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

}
